/*
 * Copyright 2010 Gal Dolber.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.unnison.framework.rebind.place;

import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.BindingImpl;
import com.google.inject.internal.Scoping;
import com.google.inject.spi.Element;

import com.unnison.framework.client.binder.contributor.RunAsync;
import com.unnison.framework.client.place.Place;
import com.unnison.framework.rebind.gin.GinContext;
import com.unnison.framework.rebind.gin.GinContributor;
import com.unnison.framework.rebind.gin.GinOracle;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

public class PlaceGinContributor implements GinContributor, PlaceProcessor {

    // Shared instance of places. For the generation
    protected static ArrayList<Class<?>> places = new ArrayList<Class<?>>();

    private static final Class<?> placeClassType = Place.class;
    private static final Class<Singleton> singletonClassType = Singleton.class;

    private GinContext ginContext;

    @Override
    public void collaborate(GinContext ginContext, TreeLogger logger, GeneratorContext context) throws UnableToCompleteException {
        this.ginContext = ginContext;

        places.clear();
        List<Element> elements = GinOracle.getElementsForModules(ginContext.getGModules());
        processPlaces(logger, elements, this);
    }

    @Override
    public void process(Class<?> placeType) {
        places.add(placeType);

        String canonicalName = placeType.getCanonicalName();
        if (placeType.isAnnotationPresent(RunAsync.class)) {
            ginContext.addAsyncProvidedType(canonicalName);
        } else {
            ginContext.addProvidedType(canonicalName);
        }
    }

    private void processPlaces(TreeLogger logger, List<Element> elements, PlaceProcessor placeProcessor) throws UnableToCompleteException {
        for (Element e : elements) {
            if (e instanceof BindingImpl<?>) {
                BindingImpl<?> binding = (BindingImpl<?>) e;
                Type type = ((TypeLiteral<?>) binding.getKey().getTypeLiteral()).getType();
                if (type instanceof Class<?> && placeClassType.isAssignableFrom((Class<?>) type)) {
                    Class<?> placeType = (Class<?>) type;

                    // TODO What about @Singleton annotation?
                    Scoping scoping = binding.getScoping();
                    if (scoping != null) {
                        Class<? extends Annotation> scopeAnnotation = scoping.getScopeAnnotation();
                        if (scopeAnnotation != null && scopeAnnotation.equals(singletonClassType)) {
                            placeProcessor.process(placeType);
                            continue;
                        }
                    }

                    logger.log(
                        com.google.gwt.core.ext.TreeLogger.Type.ERROR,
                        "All places should have Singleton scope(declared in module, not as Annotation). Found: "
                            + placeType.getCanonicalName() + " Scope: " + binding.getScoping());
                    throw new UnableToCompleteException();
                }
            }
        }
    }
}
